#ifndef UT_DEBUG_H
#define UT_DEBUG_H

//////////////////////////////////////////////////////////////////////////
#include <fstream>
#include <iosfwd>
#include <sstream>
#include <string>
#include <bitset>
#include <vector>
//////////////////////////////////////////////////////////////////////////
#ifdef WIN32
#pragma warning( disable : 4996 )
#endif

#ifdef WIN32
#ifdef LOGUTIL_EXPORTS
#   define LOGEXPORT __declspec(dllexport)
#else
#   define LOGEXPORT __declspec(dllimport)
#endif
#else
#   define LOGEXPORT __attribute__ ((visibility("default")))
#endif

namespace utility  
{
namespace log{
    typedef unsigned int uint;
    enum LogLevel {
        CRITICAL     ,
        IMPORTANT    ,
        INFO         ,
        USERCRITICAL ,
        USERIMPORTANT,
        USERINFO     ,
        DEBUGINFO    ,
        DEFAULT      ,
        LOGLEVELMAX
    };

    class LogFinisher;

    class LOGEXPORT LogMessage {
    public:
        LogMessage(LogLevel level, const char* filename, int line);
        ~LogMessage();

        LogMessage& operator<<(const std::string& value);
        LogMessage& operator<<(const char* value);
        LogMessage& operator<<(char value);
        LogMessage& operator<<(int value);
        LogMessage& operator<<(uint value);
        LogMessage& operator<<(long value);
        LogMessage& operator<<(unsigned long value);
        LogMessage& operator<<(double value);

    private:
        friend class LogFinisher;
        void Finish();

        LogLevel level_;
        const char* filename_;
        int line_;
        std::string message_;
    };

    // Used to make the entire "LOG(BLAH) << etc." expression have a void return
    // type and print a newline after each message.
    class LOGEXPORT LogFinisher {
    public:
        void operator=(LogMessage& other);
    };

    /************************************************************************/
    /* CDebugSink                                                           */
    /************************************************************************/

    // CDebugSink and derived classes are used to specify the behavior of what
    // happens to output sent to the cdbg stream. A CDebugStringBuf<>
    // object is defined in this file which derives from std::basic_stringbuf.
    // This object is used when constructing cdbg (which is a std::ostream
    // object). The stream objects can definitely get confusing. The objects
    // presented here show an easy way to hook into streams for your own
    // purposes. There are more elegant ways but they require more knowledge
    // about streams than most people need to worry about.
    //
    // CDebugSink        Abstract base class that defines a simple
    //                    debugging stream.
    // CDebugSinkNull    Does nothing
    // CDebugSinkConsole Writes debugging information to std::cout
    // CDebugSinkFile    Writes debugging information to the
    //                    specified file.
    //
    // Common overrides (of CDebugSink)
    //
    //   Header(). Override to change the standard header that is
    //             displayed, if enabled

    class LOGEXPORT CDebugSink
    {
    public:
        //////////////////////////////////////////////////////////////////////////
        // CREATORS
        //////////////////////////////////////////////////////////////////////////

        CDebugSink();
        // Default dtor is OK.
	virtual ~CDebugSink(){}

        //////////////////////////////////////////////////////////////////////////
        // MANIPULATORS
        //////////////////////////////////////////////////////////////////////////

        // Write a string to our debugging sink;
        virtual void Write(const std::string& str) = 0;
        // Write a character to our debugging sink. Usually called from
        // a std::basic_stringbuf derived object as characters are received.
        virtual void Write(int c) = 0;

        // Flush any stored information if possible for this type of sink.
        virtual void Flush()                  { }

        // By default, we emit our standard header when headers are enabled
        virtual std::string Header()          { return StandardHeader();}
        // Set the state of whether a header is written
        void ShowHeader(bool state)           {  m_bEnableHeader = state;}

    protected:
        //////////////////////////////////////////////////////////////////////////
        // MANIPULATORS
        //////////////////////////////////////////////////////////////////////////

        // The standard header consists of the current time
        std::string StandardHeader();

        //////////////////////////////////////////////////////////////////////////
        // VARIABLES
        //////////////////////////////////////////////////////////////////////////

        bool m_bEnableHeader;           ///< true to write header whenever buffer is flushed
    };

    /************************************************************************/
    /* CDebugSinkNull                                                       */
    /************************************************************************/

    class LOGEXPORT CDebugSinkNull : public CDebugSink
    {
    public:
        //////////////////////////////////////////////////////////////////////////
        // MANIPULATORS
        //////////////////////////////////////////////////////////////////////////

        static CDebugSinkNull* Instance()  { static CDebugSinkNull sink; return &sink; }

        virtual void Write(const std::string&)		{}
        virtual void Write(int)						{}

    private:
        //////////////////////////////////////////////////////////////////////////
        // CREATORS
        //////////////////////////////////////////////////////////////////////////

        CDebugSinkNull();
        // Default dtor is OK.
        virtual ~CDebugSinkNull(){}
    };

    /************************************************************************/
    /* CDebugSinkConsole                                                    */
    /************************************************************************/

    // Console sink. Data is written to std::cout
    //
    // Common overrides
    //
    //   Display(). Override to change the output source. Default is std::cout.
    //              See CDebugSinkFile for an example of this.

    class LOGEXPORT CDebugSinkConsole : public CDebugSink
    {
    public:
        //////////////////////////////////////////////////////////////////////////
        // MANIPULATORS
        //////////////////////////////////////////////////////////////////////////

        static CDebugSinkConsole* Instance()  { static CDebugSinkConsole sink; return &sink; }

        virtual void Write(const std::string& str);
        virtual void Write(int c);
        virtual void Flush();

    protected:
        //////////////////////////////////////////////////////////////////////////
        // MANIPULATORS
        //////////////////////////////////////////////////////////////////////////

        // Output the string. Derived classes can override this
        virtual void Display(const std::string& str);

        //////////////////////////////////////////////////////////////////////////
        // CREATORS
        //////////////////////////////////////////////////////////////////////////

        CDebugSinkConsole();
        virtual ~CDebugSinkConsole();

        //////////////////////////////////////////////////////////////////////////
        // VARIABLES
        //////////////////////////////////////////////////////////////////////////

        std::string m_strBuffer;
    };


    /************************************************************************/
    /* CDebugSinkConsole                                                    */
    /************************************************************************/

    class LOGEXPORT CDebugSinkFile : public CDebugSinkConsole
    {
    public:
        //////////////////////////////////////////////////////////////////////////
        // MANIPULATORS
        //////////////////////////////////////////////////////////////////////////

        static CDebugSinkFile* Instance()  { static CDebugSinkFile sink; return &sink; }

        void SetFileDir(const std::string& dir);
        void SetFileHead(const std::string& head);
        void SetFileTail(const std::string& tail);

    private:
        //////////////////////////////////////////////////////////////////////////
        // MANIPULATORS
        //////////////////////////////////////////////////////////////////////////

        virtual void Display(const std::string& str);
        void UpdateLogFile();

        //////////////////////////////////////////////////////////////////////////
        // CREATORS
        //////////////////////////////////////////////////////////////////////////

        CDebugSinkFile();
        virtual ~CDebugSinkFile();

        //////////////////////////////////////////////////////////////////////////
        // VARIABLES
        //////////////////////////////////////////////////////////////////////////
        uint        m_nLastDate;           ///< the last time when written log 
        std::string m_strDir;               ///< the directory of log file
        std::string m_strFileName;          ///< the name of log file
        std::string m_strFileHead;          ///< the head of file name
        std::string m_strFileTail;          ///< the tail of file name
        std::ofstream m_fsOutput;
    };

    /************************************************************************/
    /* CDebugSinkWindows                                                    */
    /************************************************************************/

    class LOGEXPORT CDebugSinkWindows : public CDebugSinkConsole
    {
    public:
        //////////////////////////////////////////////////////////////////////////
        // MANIPULATORS
        //////////////////////////////////////////////////////////////////////////

        static CDebugSinkWindows* Instance()  { static CDebugSinkWindows sink; return &sink; }

    private:
        //////////////////////////////////////////////////////////////////////////
        // CREATORS
        //////////////////////////////////////////////////////////////////////////

        // Default ctor and dtor is OK!

        //////////////////////////////////////////////////////////////////////////
        // MANIPULATORS
        //////////////////////////////////////////////////////////////////////////
        virtual void Display(const std::string& str);
    };

    /************************************************************************/
    /* CDebugStringBuf                                                      */
    /************************************************************************/

    // We need to derive a class from std::basic_stringbuf<> to intercept
    // characters for our debugging sinks. CDebugStringBuf bridges the
    // gap by intercepting stream characters by overriding overflow() and
    // passing the characters to the current sink

    template<class T, class Tr = std::char_traits<T>, class A = std::allocator<T> >
    class CDebugStringBuf : public std::basic_stringbuf<T, Tr, A>
    {
    private:
#ifndef WIN32
        typedef Tr traits_type;
#endif
    public:
        //////////////////////////////////////////////////////////////////////////
        // CREATORS
        //////////////////////////////////////////////////////////////////////////

        CDebugStringBuf(CDebugSink* ps = CDebugSinkConsole::Instance()) : m_poSink(ps) {}
        ~CDebugStringBuf() { m_poSink->Flush();}

        //////////////////////////////////////////////////////////////////////////
        // MANIPULATORS
        //////////////////////////////////////////////////////////////////////////

        typedef typename std::basic_stringbuf<T, Tr, A>::int_type int_type;

#ifdef _STLP_WIN32
		virtual std::streamsize xsputn(const char_type* __s, std::streamsize __n)
		{
			for(int i = 0; i < __n; i++)
				m_poSink->Write ( *(__s + i) );

			return basic_stringbuf::xsputn(__s,__n);
		}
#endif
        int_type overflow (int_type c)
        {
            if (c != traits_type::eof())
            {
                m_poSink->Write (c);
            }
            return traits_type::not_eof(c);
        }

        // Set our sink.
        void SetSink(CDebugSink* ps) { m_poSink = ps;}

    private:
        //////////////////////////////////////////////////////////////////////////
        // MANIPULATORS
        //////////////////////////////////////////////////////////////////////////

        // Do not allow copies
        CDebugStringBuf(const CDebugStringBuf&);
        CDebugStringBuf& operator=(const CDebugStringBuf&);

        //////////////////////////////////////////////////////////////////////////
        // VARIABLES
        //////////////////////////////////////////////////////////////////////////

        CDebugSink* m_poSink;       ///< current sink
    };

    /************************************************************************/
    /* CDebug                                                               */
    /************************************************************************/

    class LOGEXPORT CDebug
    {
    private:
        struct STDebugValue
        {
            bool values[LOGLEVELMAX];
            STDebugValue(){}
            STDebugValue(bool defaultValue) {for(int i = 0; i < LOGLEVELMAX; i++) values[i] = defaultValue; }
            bool & operator[](int index) { return values[index];}
            void Reset() {for(int i = 0; i < LOGLEVELMAX; i++) values[i] = false;}
            void EnableAll() {for(int i = 0; i < LOGLEVELMAX; i++) values[i] = true;}
        };
    public:
        // the enum of debug_type
        enum EDebugType
        {
            e_DefaultType      = 0,
            e_ActionType       = 1,
            e_NotifyType       = 2,
            e_NetworkType      = 3,
            e_MaxDebugType     = 32,
        };

        //////////////////////////////////////////////////////////////////////////
        // MANIPULATORS
        //////////////////////////////////////////////////////////////////////////

        static CDebug* Instance()   { static CDebug debug; return &debug; }

        // Turns off all debugging.
        void Reset();

        // Turns on/off a specific debug_type.
        void TurnOnDebugType(int type)                              { m_bsDebugType.set (type); }
        void TurnOffDebugType(int type)                             { m_bsDebugType.reset (type); }
        // Returns true if this type is enabled for debugging.
        bool IsDebugType(int pos) const                             { return m_bsDebugType.test (pos); }

        // Set/clear the debug_level of a specific debug_type.
        void EnableDebugLevel(int level, int type=e_DefaultType)       { m_vecDebugLevel[type][level] = true; }
        void DisableDebugLevel(int level, int type=e_DefaultType)       { m_vecDebugLevel[type][level] = false; }
        void EnableAllDebugLevel(int type=e_DefaultType)       { m_vecDebugLevel[type].EnableAll(); }

        void ClearDebugLevel(int type=e_DefaultType)                { m_vecDebugLevel[type].Reset(); }
        // Returns true if this level is enabled for debugging.
        //bool IsDebugLevel(int level, int type=e_DefaultType) const  { return m_vecDebugLevel[type] >= level; }
        bool IsDebugLevel(int level, int type=e_DefaultType)   { return m_vecDebugLevel[type][level]; }

        // Set our sink.
        void SetSink(CDebugSink* ps)                                { m_oDebugStream.SetSink(ps); }
        // Get the debug_stream.
        CDebugStringBuf<char>* GetDebugStream()                     { return &m_oDebugStream; }

        void OutputFileName(bool value)                                 { m_bOutputFileName = value; }
        bool OutputFileName()                                           { return m_bOutputLineNumber; }
        void OutputLineNumber(bool value)                               { m_bOutputLineNumber = value; }
        bool OutputLineNumber()                                         { return m_bOutputLineNumber; }

    private:
        //////////////////////////////////////////////////////////////////////////
        // CREATORS
        //////////////////////////////////////////////////////////////////////////

        CDebug();
        // Default dtor is OK.

        //////////////////////////////////////////////////////////////////////////
        // VARIABLES
        //////////////////////////////////////////////////////////////////////////


        std::bitset<e_MaxDebugType>    m_bsDebugType;        ///< the bitset of debug_type
        std::vector<STDebugValue>      m_vecDebugLevel;      ///< the vector debug_level
        CDebugStringBuf<char>          m_oDebugStream;       ///< the debug_stream
        bool                           m_bOutputLineNumber;  ///< whether output the line number
        bool                           m_bOutputFileName;    ///< whether output the file name
    };


    /************************************************************************/
    /*                                                                      */
    /************************************************************************/

    // You can use cdbg just like std::cout. The default sink is
    // CDebugSinkConsole, the default debug_level is e_DefaultDebugLevel and
    // all debug_type is turn off except for e_DefaultType.
    // You can use cdbg after you set the debug_type and debug_level, ex:
    //
    //     GSUT::CDebug::Instance()->TurnOnDebugType(GSUT::CDebug::e_DefaultType);
    //     GSUT::CDebug::Instance()->TurnOnDebugType(GSUT::CDebug::e_ActionType);
    //     GSUT::CDebug::Instance()->SetDebugLevel(GSUT::CDebug::e_DbgInfoLevel,
    //                                             GSUT::CDebug::e_ActionType);

    extern std::ostream cdbg;
} // namespace utility::log
} // namespace utility

#define LOG(LEVEL)  utility::log::LogFinisher()=utility::log::LogMessage(utility::log::LEVEL, __FILE__, __LINE__)
#endif // UT_DEBUG_H
